#include "api/api.h"
#include "basics/error_handle.h"
#include "basics/macros.h"
#include "cgroup/cg_utils.h"
#include "netns/netns.h"
#include "parse_api.h"
#include <assert.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <unistd.h>

#define MAX_REQS 32

int main(int argc, char *argv[], char *const envp[]) {
    pid_t pid = fork();
    if (pid < 0) {
        fprintf(stderr, "[neodocker]: can not start daemon\n");
        return EXIT_FAILURE;
    } else if (pid > 0) {
        return EXIT_SUCCESS;
    }
    if (setsid() < 0) {
        return EXIT_FAILURE;
    }
    int nullfd = open("/dev/null", O_RDWR);
    int logfd = open(".log", O_CREAT | O_WRONLY | O_TRUNC,
                     S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH);
    int errlogfd = open(".errlog", O_CREAT | O_WRONLY | O_TRUNC,
                        S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH);
    check(logfd != -1);
    check(errlogfd != -1);
    // check(close(STDIN_FILENO) != -1);
    // check(close(STDOUT_FILENO) != -1);
    // assert(close(STDERR_FILENO) != -1);
    assert(dup2(nullfd, STDIN_FILENO) != -1);
    assert(dup2(logfd, STDOUT_FILENO) != -1);
    assert(dup2(errlogfd, STDERR_FILENO) != -1);

    /// cgroup init
    cgroup_init();

    /// socket process
    create_netns_host_bridge();

    unlink(SOCK_NAME); // to avoid an old socket exist, no error checking
    /// unix sockaddr
    struct sockaddr_un server_addr, client_addr;
    server_addr.sun_family = AF_UNIX;
    strcpy(server_addr.sun_path, SOCK_NAME);
    /// create an unix socket, which is the best one to use locally
    int server_socket = socket(AF_UNIX, SOCK_STREAM, 0), client_socket;
    check(server_socket != -1);
    if_fail(bind(server_socket, (struct sockaddr *)&server_addr,
                 sizeof(server_addr)) != -1,
            goto err);
    check(chmod(SOCK_NAME, S_IRWXU | S_IRWXG | S_IRWXO) != -1);
    /// daemon listen to front end actions
    check(listen(server_socket, MAX_REQS) != -1);
    socklen_t client_len = sizeof(client_addr);
    while (1) {
        if_fail((client_socket =
                     accept(server_socket, (struct sockaddr *)&client_addr,
                            &client_len)) != -1,
                goto err);
        enum API_ID id;
        char recv_buf[BUFF_MAX];
        /// recv the call id first
        if_fail(recv(client_socket, &id, sizeof(id), 0) != -1, goto err);
        /// send a success signal back
        if_fail(send(client_socket, recv_buf, 1, 0) != -1, goto err);
        // /// recv real params
        // if_fail(recv(client_socket, recv_buf, BUFF_MAX, 0) != -1, goto err);
        /// parse params and run
        switch (id) {
        case CNTR_CREATE:
            parse_create(client_socket);
            break;
        case CNTR_ATTACH:
            parse_attach(client_socket);
            break;
        case CNTR_DELETE:
            parse_delete(client_socket);
            break;
        case CNTR_UPDATE:
            parse_update(client_socket);
            break;
        }
    }
err:
    fprintf(stderr, "[neodocker]: daemon failed\n");
    check(close(server_socket) != -1);
    check(unlink(SOCK_NAME) != -1);
    check(close(logfd) != -1);
    /// errlogfd close fail can't have errlog printed in the file anymore
    assert(close(errlogfd) != -1);

    /// cgroup exit
    cgroup_exit();

    return EXIT_SUCCESS;
}
